/**
 * Copyright (C)  2011 Kent Tong <freemant2000@yahoo.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.ttdev.ecofoods;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

public class DefaultFoodsService implements FoodsService {
	private static String DB_FILENAME = "foods.db";
	private static final String TABLE_FOODS = "foods";
	private static final String TAG = DefaultFoodsService.class.getName();
	private LocalesService ls;
	private AssetProvider ap;

	public void init() {
		if (!DBExists()) {
			Log.i(TAG, "Database " + getDBPath() + " not found");
			createDB();
			if (!DBExists()) {
				Log.e(TAG, "Database was not created");
			}
		}
	}

	private boolean DBExists() {
		return new File(getDBPath()).exists();
	}

	private void createDB() {
		Log.i(TAG, "Creating database " + getDBPath());
		makeDBDir();
		copyDBFromAsset();
	}

	private void makeDBDir() {
		new File(getDBPath()).getParentFile().mkdir();
	}

	private void copyDBFromAsset() {
		try {
			InputStream in = ap.open(DB_FILENAME);
			OutputStream out = new FileOutputStream(getDBPath());
			byte[] buf = new byte[1024];
			for (;;) {
				int noBytesRead = in.read(buf);
				if (noBytesRead == -1) {
					break;
				}
				out.write(buf, 0, noBytesRead);
			}
			in.close();
			out.flush();
			out.close();

		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public void setLs(LocalesService ls) {
		this.ls = ls;
	}

	public List<Food> findFoodRecommendations(String keyword) {
		List<Food> foods = new ArrayList<Food>();
		SQLiteDatabase db = openDatabase();
		try {
			Cursor c = sendQuery(db, keyword);
			try {
				c.moveToFirst();
				FoodColumns cols = new FoodColumns(ls, c);
				while (!c.isAfterLast()) {
					Food food = loadFood(c, cols);
					foods.add(food);
					c.moveToNext();
				}
				return foods;
			} finally {
				c.close();
			}
		} finally {
			db.close();
		}
	}

	private SQLiteDatabase openDatabase() {
		return SQLiteDatabase.openDatabase(getDBPath(), null,
				SQLiteDatabase.OPEN_READONLY);
	}

	private Food loadFood(Cursor c, FoodColumns cols) {
		Long id = c.getLong(cols.getIdColIdx());
		String name = c.getString(cols.getNameColIdx());
		String scientificNames = c.getString(cols.getscientificNamesColIdx());
		String desc = c.getString(cols.getDescColIdx());
		Origin origin;
		String originString = c.getString(cols.getOriginColIdx());
		try {
			origin = Origin.valueOf(originString);
		} catch (IllegalArgumentException e) {
			origin = Origin.UNKNOWN;
			Log.w(TAG, "Unknown origin: " + originString);
		}
		Cultivation cult;
		String cultString = c.getString(cols.getCultColIdx());
		try {
			cult = Cultivation.valueOf(cultString);
		} catch (IllegalArgumentException e) {
			cult = Cultivation.UNKNOWN;
			Log.w(TAG, "Unknown cultivation: " + cultString);
		}
		String recommendKey = c.getString(cols.getRecommendKeyColIdx());
		Recommendation r = Recommendation.find(recommendKey);
		Food food = new Food(id, name, scientificNames, desc, origin, cult, r);
		return food;
	}

	private String getDBPath() {
		return AndroidDBUtil.getDBPath("com.ttdev.ecofoods", DB_FILENAME);
	}

	private Cursor sendQuery(SQLiteDatabase db, String keyword) {
		String cond = "";
		for (Locale locale : ls.getSupportedLocales()) {
			if (!cond.equals("")) {
				cond += " or ";
			}
			String nameColumn = "name_" + locale.toString();
			cond += nameColumn + " like '%" + keyword + "%'";
		}
		// sqlite doesn't have built-in Chinese collation support. Instead, it
		// just compares them as bytes. But it still has the good effect of
		// grouping the same characters together.
		String nameColumnInBestLocale = "name_" + ls.getBestLocale().toString();
		String recommendationIdx = "case recommendation when \"ok\" then 0 when \"think_twice\" then 1 else 2 end";
		String orderBy = recommendationIdx + ", " + nameColumnInBestLocale;
		return db.query(TABLE_FOODS, null, cond, null, null, null, orderBy);
	}

	public void setAp(AssetProvider ap) {
		this.ap = ap;
	}

	public Food getFood(Long id) {
		SQLiteDatabase db = openDatabase();
		try {
			Cursor c = db.query(TABLE_FOODS, null, "_id=" + id, null, null,
					null, null);
			try {
				c.moveToFirst();
				FoodColumns cols = new FoodColumns(ls, c);
				if (!c.isAfterLast()) {
					return loadFood(c, cols);
				} else {
					return null;
				}
			} finally {
				c.close();
			}
		} finally {
			db.close();
		}
	}
}
